package trader

import (
	"fmt"
	"github.com/adshao/go-binance"
	"strconv"
	"time"
	"trader-bot/src/alog"
	"trader-bot/src/service"
)

type traderInfo struct {
	time 		int64
	orderId 	int64
	price 		float64
}

func Cancel(t int64,symbol string) error {
	s:= service.NewService()
	ol := s.ListOrder(symbol)
	for _,v := range ol {
		if v.Side == string(binance.SideTypeSell) {
			if(s.Time() - v.Time) > t {
				if s.Cancel(symbol,v.OrderID) {
					s.SellMarket(symbol,strconv.FormatFloat(s.Cur(symbol),'f',2,64),v.OrigQuantity)
				}
			}
		}
		time.Sleep(time.Second * 300)
	}
	return nil
}

func Trader(symbol,interval string ,limit int,profit float64,rate float64,step int,total float64,wait uint) error{
	s:= service.NewService()
	avg ,err:= s.Avg(symbol,interval,limit)
	if err != nil {
		return err
	}

	cur := s.Cur(symbol)
	if cur == 0 {
		return fmt.Errorf("Can't Get Cur Price of : %s",symbol)
	}

	start := 1

	buyOrder := 0
	ol := s.ListOrder(symbol)
	for i,_ := range ol {
		if ol[i].Side == string(binance.SideTypeSell) {
			buyOrder++
		} else {
				s.Cancel(symbol,ol[i].OrderID)
		}
	}
	start = start + buyOrder

	var stepMap= make(map[int]traderInfo)

	var quality = total / float64(step)

	alog.Info("start trader...\n")
	alog.Info(fmt.Sprintf("start--: symbol: %s, interval:%s, limit:%d ,step:%d,profile:%f ,quality: %f.\n",symbol,interval,limit,step,profit,total))

	for i := 0; i< step; i++ {

		alog.Infof("Get Current Price = %f.\n",cur)
		alog.Infof("Get Avg Price = %f.\n",avg)

		try := avg * ( 1 - (float64(i) + float64(start) ) / 100  * rate)
		alog.Info(fmt.Sprintf("Trying to buy step %d price = %f.\n",i+1,try))

		if cur < try {
			if !Validate(quality,"USDT") {
				alog.Error("not enough balance")
				break
			}

			buyCount := quality / cur

			order,err := s.BuyLimit(symbol,strconv.FormatFloat(cur,'f',8,64),
					                       strconv.FormatFloat(buyCount,'f',2,64))
			if err != nil {
				alog.Error(fmt.Sprintf("buy %s quality:%f failed,%s",symbol,quality,err.Error()))
				return err
			}
			alog.Info(fmt.Sprintf("Buy %s quality:%f success.\n",symbol,buyCount))
			stepMap[i] = traderInfo{
				time:s.Time(),
				orderId:order,
				price:cur,
			}
		}
	}

	time.Sleep(time.Second * 3)

	for i,_:=range stepMap {
		info := stepMap[i]
		order := s.QueryOrder(symbol,info.orderId)
		alog.Info(fmt.Sprintf("order = %+v",order))
		if order.Status == "FILLED" {
			sellPrice := info.price * (1 + profit / 100)
			sellPriceStr := strconv.FormatFloat(sellPrice,'f',2,64)
			sellCount := GetSellCount(order.ExecutedQuantity)
			_,err:= s.SellLimit(symbol,sellPriceStr,sellCount)
			if err != nil {
				alog.Errorf("sell %s quality:%s price:%s failed,%s",symbol,sellCount,sellPriceStr,err)
				return err
			}
			alog.Infof("sell order %s quality:%s,price %s.\n",symbol,sellCount,sellPriceStr)
		}
	}

	time.Sleep(time.Second * time.Duration(wait))


	return nil
}

func Validate(quality float64,symbol string) bool {
	s := service.NewService()
	b := s.GetBalance(symbol)
	if b == service.Zero {
		return false
	}

	bb,_:= strconv.ParseFloat(b,64)
	if bb > quality {
		return true
	}
	alog.Info("not enough balance")
	return  false
}


func GetSellCount(quality string) string {
	commission := 0.001

	q,_ := strconv.ParseFloat(quality,64)

	return strconv.FormatFloat(q * (1- commission),'f',2,64)
}